﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Green;
using System.Xml.Linq;
using System.Drawing;

namespace Noris.Tools.TraceVisualiser.Green.SqlServer
{
    #region TraceReader : reader pro soubory typu Log blokování na aplikačním serveru
    /// <summary>
    /// TraceReader : reader pro soubory typu Log blokování na aplikačním serveru
    /// </summary>
    public class TraceReader : ITraceReader, IColorDeclarator
    {
		#region IPlugin Members
		/// <summary>
		/// Příznak, že Plugin je aktivní ve standardním běhu (tj. při běžném užiavtelském spuštění).
		/// Plně funkční pluginy tedy vrací true.
		/// Pluginy, které jsou ve vývoji, vrací false, takové jsou zobrazeny / spouštěny pouze v aplikaci, která běží v režimu Debug.
		/// </summary>
		bool IPlugin.Active { get { return true; } }
		#endregion
		#region ITraceReader, IDisposable Members
        /// <summary>
        /// Zde reader vrací výraz, který se použije v dialogu při výběru vstupních trace soubor.
        /// Různé readery mají různé vstupní soubory s různou příponou, a my nechceme žádný reader opomenout.
        /// Reader může umět zpracovávat více různých koncovek, proto se používá string[], každá položka obsahuje jeden filtr.
        /// Každý jednotlivý filtr je ve tvaru: "Textový popisek|filtr", například: "Aplikační trace Green (*.csv)|*csv".
        /// </summary>
        string[] ITraceReader.FileOpenFilters
        {
            get
            {
                return new string[]
                {
                    "SqlServer Trace exported files (*.xml)|*.xml"
                };
            }
        }
        /// <summary>
        /// Metoda má za úkol podívat se na název souboru,
        /// a vyjádřit se k tomu, zda se bude chtít podívat i dovnitř souboru (tím, že nastaví args.CheckContent = true).
        /// Metoda by měla (pokud se tento TraceReader dokáže s názvem souboru vypořádat) rozebrat název souboru na jméno serveru, a oddělit i pořadové číslo z názvu.
        /// Metoda absolutně nemá za úkol otevírat soubor, pracuje jen s názvem.
        /// Pokud se metoda bude chtít podívat do souboru, nastaví args.CheckContent = true, a za chvíli se vyvolá další metoda: this.CheckContent().
        /// </summary>
        /// <param name="args">Data o souboru (název a výsledky této metody)</param>
        /// <returns></returns>
        void ITraceReader.CheckFileName(TraceReaderCheckNameArgs args)
        {
            // Nechci se dívat dovnitř (možná časem změním názor):
            args.CheckContent = false;

            // Přípona má být ".xml"
            System.IO.FileInfo fileInfo = args.FileInfo;
            if (!String.Equals(fileInfo.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
                return;

            // Na jméno souboru nemáme žádné nároky.
            // Každý XML soubor zkusíme otevřít, a až podle jeho záhlaví budeme usuzovat na to, zda je to náš soubor nebo někoho jiného.

            // Určíme název serveru:
            // Server se zde má na mysli databázový, a ne aplikační. To proto, že (ačkoliv data generuje aplikační server do svého adresáře), jde o data SQL serveru.
            // Název serveru tedy bude dán fixní = "SqlServer", bez přidání jména serveru či databáze (jedna každá databáze bude tvořit oddělený proces v rámci serveru).
            args.ServerName = SqlServerConstants.ServerName;

            // Šlo by to, podíváme se dovnitř:
            args.CheckContent = true;
        }
        /// <summary>
        /// Metoda je volaná tehdy, když tento reader se přihlásil ke čtení záhlaví souboru (v metodě CheckFileName, nastavil args.CheckContent = true).
        /// Tato metoda nemá za úkol číst data, ale podívat se dovnitř, a usoudit, zda my chceme tento soubor zpracovávat.
        /// Soubor je otevřen, a je k němu přístup přes argument
        /// </summary>
        /// <param name="args"></param>
        void ITraceReader.CheckContent(TraceReaderCheckContentArgs args)
        {
            // Nechci soubor zpracovávat (možná časem změním názor):
            args.Priority = 0M;

            // Ze souboru si načtu prvních několik řádků (konkrétně 4) a prověřím, zda jsou OK:
            string title = args.ReadFirstLine().Trim();                 // První řádek načtu jinak
            if (title != "<?xml version=\"1.0\" encoding=\"utf-16\"?>") return;
            string line = args.FileStreamReader.ReadLine().Trim();      // Další řádky načítám už standardně
            if (line  != "<TraceData xmlns=\"http://tempuri.org/TracePersistence.xsd\">") return;
            line = args.FileStreamReader.ReadLine().Trim();
            if (line != "<Header>") return;
            line = args.FileStreamReader.ReadLine().Trim();
            if (!line.StartsWith("<TraceProvider name=\"Microsoft SQL Server\" MajorVersion")) return;        // Řádek takhle jen začíná, pokračování je proměnlivé, to rozebereme poté.  Typicky: <TraceProvider name="Microsoft SQL Server" MajorVersion="10" MinorVersion="50" BuildNumber="1600" />

            // Najdu <ServerInformation name="servername" />
            string serverInfo = CreateServerName(this.DetectServerInfoSqlTrace(args.FileStreamReader));

            // Najdu začátek a konec dat v souboru:
            TimeRange contentTime = DetectTimeRangeSqlTrace(args.FileStreamReader);

            // Pokud jsem našel nějaká data, pak bych takový soubor mohl zpracovat:
            if (contentTime.Begin > DateTime.MinValue)
            {
                args.Priority = 1M;
                args.ContentTime = contentTime;
                args.TraceType = SqlServerConstants.TraceType;           // Text "SqlServerTrace", na nějž je pak citlivá logika donačítání dat pro potřebné řádky
                args.ServerTimePriority = 1;                             // Tento server má nejdůvěryhodnější čas
                args.UserData.Add("ServerInformation", serverInfo);      // Název SQL serveru se dostane do všech návazných argumentů (do metod this.CheckContent())
				this._PrepareContentElementTexts(args);
                this._PrepareContentTreePaths(args);
            }
        }
        /// <summary>
        /// Sestaví texty, které se budou vypisovat v elementu a v tooltipu
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentElementTexts(TraceReaderCheckContentArgs args)
        {
            string serverInfo = (string)args.UserData["ServerInformation"];
            string fileName = args.FileInfo.FullName;
            string bareName = Path.GetFileNameWithoutExtension(fileName);
            string onlyName = args.FileInfo.Name;
            string pathDir1 = args.FileInfo.Directory.Name;         // Jméno prvního up adresáře
            string fileDir1 = Path.Combine(pathDir1, onlyName);     // jméno s prvním adresářem a s příponou

            // Texty do elementu:
            args.ElementTexts = new string[] { serverInfo, bareName, onlyName, fileDir1, fileName };

            // Tooltip:
            args.ElementTooltip = "Vstupní soubor: " + fileDir1 + Environment.NewLine +
                "Obsahuje data za čas: " + args.ContentTime.StringShort;
        }
        /// <summary>
        /// Do argumentu připraví nody, které budeme později pravděpodobně používat:
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentTreePaths(TraceReaderCheckContentArgs args)
        {
            // Plná cesta k nodu, absolutní (true), node = Klienti, graf = elementy se nenačítají, podřízené řádky se načítají dynamicky, třídí se podle textu:
            TreePath nodeBlock = new TreePath(true);
            nodeBlock.AddItem(CreateTreePathItemSql());

            // Připravené dva nody dáme do TreePaths:
            args.TreePaths = new TreePath[] { nodeBlock };
        }
        /// <summary>
        /// Metoda je volaná pro zpracování celého obsahu dat.
        /// Veškerou podporu má v argumentu TraceReaderReadItemArgs args.
        /// Metoda si může libovolně zpracovat vstupující soubor 
        /// a vytvářet z něj data ve formě jednotlivých záznamů typu ITraceItem.
        /// Tyto záznamy předává ke zpracování do datového systému pomocí volání metody argumentu ProcessItem(ITraceItem item).
        /// Metoda takto může vynechat některé věty (nedávat je ke zpracování), anebo některé věty zpracovat více způsoby
        /// (například z jedné věty trace vytvoří dvě položky: jednu ktertá představuje aktivitu uživatele, a druhou která popisuje stav paměti).
        /// Tím, že zpracování probíhá v jedné metodě, si reader může udržovat kontinuální povědomí o načítaných datech, 
        /// a například o stavu paměti posílat položky při změně hodnoty (není třeba posílat 1000 x jednu hodnotu stavu paměti).
        /// </summary>
        /// <param name="args"></param>
        void ITraceReader.ProcessContent(TraceReaderProcessContentArgs args)
        {
            this._ProcessContent(args);
        }
        /// <summary>
        /// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
        /// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
        /// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
        /// </summary>
        IEventItem ITraceReader.CreateIEventItem(IEventEdge data)
        {
            return _CreateIEventItem(data);
        }
        /// <summary>
        /// Metoda, která má za úkol vytvořit celou plně definovanou cestu k nodu.
        /// Tato metoda se volá zcela výjimečně - tehdy, když tento objekt předepisuje nějakou TargetPath, která dosud neexistuje.
        /// Pak si jádro vyžádá plnou cestu k nodu, včetně textů a definice všech nodů.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        void ITraceReader.CreateFullTreePath(TraceReaderCreatePathArgs args)
        {
            this.CreateFullTreePath(args);
        }
        void IDisposable.Dispose()
        {
        }
        #endregion
        #region Podpora pro načítání datumů ze souboru
        protected string DetectServerInfoSqlTrace(System.IO.StreamReader sr)
        {
            string serverInfo = null;
            string findName = "<ServerInformation name=\"";
            while (serverInfo == null && !sr.EndOfStream)
            {
                string line = sr.ReadLine().Trim();
                int at = line.IndexOf(findName);
                if (at >= 0)
                {
                    line = line.Substring(at + findName.Length);
                    at = line.IndexOf('"');
                    if (at > 0)
                        serverInfo = line.Substring(0, at);
                }
            }
            return serverInfo;
        }
        internal static TimeRange DetectTimeRangeSqlTrace(System.IO.StreamReader sr)
        {
            DateTime? begin = null;
            DateTime? end = null;
            string findBeg = "name=\"StartTime\">";
            string findEnd = "name=\"EndTime\">";

            // Hledám Begin dokud ho nenajdu (do konce souboru):
            while (!begin.HasValue && !sr.EndOfStream)
            {
                string line = sr.ReadLine();
                if (line.Contains(findBeg))
                    begin = ReadSqlTimeFromText(line, findBeg);
            }

            // Hledám End od konce, v blocích po 4KB:
            long findFrom = sr.BaseStream.Length;
            int block = 4096;
            while (!end.HasValue && findFrom > 0L)
            {   // Hledám někde na konci souboru, začínám na posledních 2 KB, a pokud v nich nic nenajdu, tak se podívám o další 2KB směrem k začátku:
                findFrom = (findFrom < block ? 0L : findFrom - block);
                sr.DiscardBufferedData();                                      // Zahodím buffery StreamReader, půjdu pro nová data natvrdo do souboru
                sr.BaseStream.Seek(findFrom, System.IO.SeekOrigin.Begin);

                while (!sr.EndOfStream)
                {   // Hledám vždy až do konce souboru, neskončím na první nalezené hodnotě End (někde dál může být větší hodnota End):
                    string line = sr.ReadLine();
                    DateTime? date = null;
                    if (line.Contains(findEnd))
                        date = ReadSqlTimeFromText(line, findEnd);
                    else if (line.Contains(findBeg))
                        date = ReadSqlTimeFromText(line, findBeg);
                    if (date.HasValue && (!end.HasValue || (end.HasValue && date.Value > end.Value)))
                        end = date;
                }
            }

            // Vyhodnocení:
            if (begin.HasValue && end.HasValue)
                return new TimeRange(begin.Value, end.Value);
            if (begin.HasValue)
                return new TimeRange(begin.Value, begin.Value);
            return TimeRange.Empty;
        }
        /// <summary>
        /// Vrátí datum a čas z daného textu řádku, který následuje hned za daným textem atributu.
        /// Typický vstup:
        /// «Column id="14" name="StartTime"»2012-02-08T16:04:19.277+01:00«/Column»
        /// Typický attr:
        /// name="StartTime"»
        /// </summary>
        /// <param name="line"></param>
        /// <param name="attr"></param>
        /// <returns></returns>
		internal static DateTime? ReadSqlTimeFromText(string line, string attr)
        {
            DateTime? result = null;
            int at = line.IndexOf(attr);
            int ab = at + attr.Length;
            if (at >= 0 && (ab + 22 < line.Length))
            {
                string text = line.Substring(at + attr.Length);       // 2012-02-08T16:04:19.277+01:00</Column>
                int ae = text.IndexOf("<");
                if (ae > 0)
                    text = text.Substring(0, ae);                     // 2012-02-08T16:04:19.277+01:00
                result = ParseUtcDateTime(text);
            }
            return result;
        }
        /// <summary>
        /// Konevrtuje datum a čas z textu SQL trace na DateTime
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
		internal static DateTime? ParseUtcDateTime(string text)
        {
            DateTime? result = null;
            int at = text.IndexOf("+");
            if (at > 0)
                text = text.Substring(0, at);
            if (text.Length > 23)
                text = text.Substring(0, 23);
			if (text.Length >= 19)
			{
				if (text.Length == 19)             // Délka 19 = "yyyy-MM-ddTHH:mm:ss"
					text += ".000";                //             12345678901234567890123456789
				else if (text.Length < 23)         // Délka 24 = "yyyy-MM-ddTHH:mm:ss.fff"
					text = text.PadRight(23, '0'); //             12345678901234567890123456789
				DateTime pars;
				if (DateTime.TryParseExact(text, "F", DtFi, System.Globalization.DateTimeStyles.NoCurrentDateDefault | System.Globalization.DateTimeStyles.AllowWhiteSpaces, out pars))
					result = pars;
			}
            return result;
        }
        /// <summary>
		/// FormatInfo pro konverzi SQL datetime do .NET (pro formát "F", hodnota FullDateTimePattern = "yyyy-MM-dd'T'HH:mm:ss.fff")
        /// </summary>
		internal static System.Globalization.DateTimeFormatInfo DtFi
        {
            get
            {
                if (__Dtfi == null)
                {
                    System.Globalization.DateTimeFormatInfo dfi = new System.Globalization.DateTimeFormatInfo();
                    dfi.FullDateTimePattern = "yyyy-MM-dd'T'HH:mm:ss.fff";
                    __Dtfi = dfi;
                }
                return __Dtfi;
            }
        }
		private static System.Globalization.DateTimeFormatInfo __Dtfi;
        #endregion
		#region Fyzické načítání obsahu souboru a zpracování na položky ITraceItem
		/// <summary>
		/// Zpracuje data do jednotlivých položek
		/// </summary>
		/// <param name="args"></param>
		private void _ProcessContent(TraceReaderProcessContentArgs args)
		{
			TraceServerPersistData persistData = TraceServerPersistData.GetFrom(args.Server.Tag);
			try
			{
				this.StringHeap = args.Main.StringHeap;
                this.LineNumber = 0;


                // Data jsou ve formátu XML, budu je načítat přes System.Xml.Linq.XDocument:
                // Struktura dokumentu je:
                //  TraceData
                //      Header
                //          TraceProvider
                //          ServerInformation
                //          ProfilerUI
                //              OrderedColumns
                //              TracedEvents
                //                  Event
                //                      EventColumn
                //      Events
                //          Event
                //              Column
                XDocument xDoc = System.Xml.Linq.XDocument.Load(args.FileStreamReader);
                this.XmlNameSpace = xDoc.Root.Name.Namespace.NamespaceName;
                XElement xTraceData = xDoc.FirstNode as XElement;
                if (xTraceData.Name.LocalName == "TraceData")
                {
					XElement xHeader = xTraceData.Element(GetXName("Header"));
                    if (xHeader != null)
                        _ProcessHeader(args, xHeader, ref persistData);

					XElement xEvents = xTraceData.Element(GetXName("Events"));
                    if (xEvents != null)
						this._ProcessEvents(args, xEvents, ref persistData);
                }
			}
			finally
			{
				args.Server.Tag = persistData;
				this.StringHeap = null;
			}
		}
		/// <summary>
		/// Zpracuje hlavičku (element Headers)
		/// </summary>
		/// <param name="args"></param>
		/// <param name="xHeader"></param>
        /// <param name="persistData"></param>
		private void _ProcessHeader(TraceReaderProcessContentArgs args, XElement xHeader, ref TraceServerPersistData persistData)
        {
            XName xn = xHeader.Name;

			persistData.Provider = "";
            XElement xProvider = xHeader.Element(GetXName("TraceProvider"));
            if (xProvider != null)
            {
                XAttribute xName = xProvider.Attribute("name");
                XAttribute xMajor = xProvider.Attribute("MajorVersion");
                XAttribute xMinor = xProvider.Attribute("MinorVersion");
                XAttribute xBuild = xProvider.Attribute("BuildNumber");
				persistData.Provider =
                    (xName == null ? "???" : xName.Value) + " " +
                    (xMajor == null ? "??" : xMajor.Value) + "." +
                    (xMinor == null ? "??" : xMinor.Value) + " (build " +
                    (xBuild == null ? "????" : xBuild.Value) + ")";
            }

			persistData.ServerInformation = "";
            XElement xServInfo = xHeader.Element(GetXName("ServerInformation"));
            if (xServInfo != null)
            {
				XAttribute xName = xServInfo.Attribute("name");
                if (xName != null)
                    persistData.ServerInformation = CreateServerName(xName.Value);
            }

            UserData userData = args.Trace.UserData;
            if (userData.ContainsKey("TraceProvider"))
				userData["TraceProvider"] = persistData.Provider;
            else
				userData.Add("TraceProvider", persistData.Provider);

			if (persistData.ServerInformation != "")
            {
                if (userData.ContainsKey("ServerInformation"))
					userData["ServerInformation"] = persistData.ServerInformation;
                else
					userData.Add("ServerInformation", persistData.ServerInformation);
            }
        }
        /// <summary>
        /// Vrátí jméno serveru
        /// </summary>
        /// <param name="serverInformation"></param>
        /// <returns></returns>
        internal static string CreateServerName(string serverInformation)
        {
            if (String.IsNullOrEmpty(serverInformation)) return "";
            if (serverInformation.Contains('\\'))
                return serverInformation.Replace('\\', '/');
            return serverInformation;
        }
		/// <summary>
		/// Načte všechny eventy z XML dat, z nodu Events
		/// </summary>
		/// <param name="args"></param>
		/// <param name="xEvents"></param>
		/// <param name="persistData"></param>
		private void _ProcessEvents(TraceReaderProcessContentArgs args, XElement xEvents, ref TraceServerPersistData persistData)
        {
			IEnumerable<XElement> xEventList = xEvents.Elements(GetXName("Event"));
			foreach (XElement xEvent in xEventList)
            {
                this.LineNumber++;
				EventItem eventItem = EventItem.CreateItem(args.Main.StringHeap, args.Trace, this.LineNumber, xEvent, ref persistData);
				if (eventItem == null) continue;

				// Některé eventy jsou kompletní, některé jsou Begin/End. Tady si je uchovám bokem v rámci persistoru a do systému vkládám až spárované:
                if (eventItem.EventPart == EventPartType.Complete)
    				args.ProcessEvent(eventItem);
			}
        }
		/// <summary>
		/// Reference na zásobník textů
		/// </summary>
		protected StringHeap StringHeap { get; set; }
		/// <summary>
		/// Namespace v XML dokumentu. 
		/// Určí se na začátku načítání, používá se pro tvorbu XName v metodě this.GetXName().
		/// </summary>
		protected string XmlNameSpace { get; set; }
		/// <summary>
		/// Vrátí kompletní XName = dodané localName + namespace this.XmlNameSpace.
		/// </summary>
		/// <param name="localName"></param>
		/// <returns></returns>
		protected XName GetXName(string localName)
		{
			return XName.Get(localName, this.XmlNameSpace);
		}
		/// <summary>
		/// Číslo řádky = číslo eventu
		/// </summary>
		protected int LineNumber { get; set; }
		#endregion
		#region Podpora pro tvorbu nodů grafu (řádky: klíče, názvy, definice grafiky a chování řádku)
		/// <summary>
		/// Sestavení plné cesty k nodu na základě jejího stringu
		/// </summary>
		/// <param name="args"></param>
		protected void CreateFullTreePath(TraceReaderCreatePathArgs args)
		{
			// Připravím si polotovar (tam se mi rozeberou jednotlivé složky cesty):
			TreePath input = new TreePath(args.Path);
			List<TreePathItem> items = input.Items;

			// Připravím si výsledek:
			TreePath result = new TreePath(input.AbsolutePath);
			string status = (input.AbsolutePath ? "root" : "node");           // Jakou část cesty budu nyní zpracovávat? Dynamicky se posouvá podle toho, jak se posouvám na konkrétní cestě
			foreach (TreePathItem item in items)
			{
				switch (status)
				{
					case "root":
						// Řeším node v root úrovni:
						// Podle klíče položky určím, co v ní je, a jakého typu bude příští položka:
						switch (item.Key)
						{
							case SqlServerConstants.NodeSqlServer:
								result.AddItem(TraceReader.CreateTreePathItemSql());
								status = "serverinfo";         // V příští úrovni cesty (další TreePathItem item in items) očekávám název serveru
								break;
						}
						break;
                    case "serverinfo":
						// V této úrovni očekávám ServerInfo:
						result.AddItem(TraceReader.CreateTreePathItemServer(args, item));
						status = "spid";                       // V příští úrovni cesty bude jméno objektu
						break;
                    case "spid":
                        // V této úrovni očekávám SPID:
                        result.AddItem(TraceReader.CreateTreePathItemSpid(args, item));
						status = "detail";                     // V příští úrovni cesty budou detailní položky (???)
						break;
					default:
						result.AddItem(TraceReader.CreateTreePathItemDefault(item.Key));
						break;
				}
			}
			args.TreePath = result;
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "SQL trace", která se zobrazuje v root úrovni grafu.
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemSql()
		{
            string name = "SQL trace";
            return new TreePathItem(SqlServerConstants.NodeSqlServer, name,
                GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.ChildSortedText));
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "Název serveru", která se zobrazuje jako podřízená položka k root nodu "SQL trace".
		/// </summary>
		/// <returns></returns>
		private static TreePathItem CreateTreePathItemServer(TraceReaderCreatePathArgs args, TreePathItem item)
		{
            string name = item.Key;                    // Název objektu = "sirius_2008"
            GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.DynamicElementsLoading | RowProperties.ChildSortedKey);
			dcl.RowHeightTotalMinPx = 24;
            return new TreePathItem(item.Key, name, dcl);
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "SPID", která se zobrazuje jako podřízená položka k nodu "Název serveru".
		/// </summary>
		/// <returns></returns>
        private static TreePathItem CreateTreePathItemSpid(TraceReaderCreatePathArgs args, TreePathItem item)
		{
            string name = item.Key;                    // Název objektu = "0058"

            GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(12, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading);
            dcl.RowHeightTotalMinPx = 16;
            dcl.RowHeightTotalMaxPx = 200;
            dcl.ShowPrevNextIcon = true;
            return new TreePathItem(item.Key, name, dcl);
		}
		/// <summary>
		/// Vrátí definici pro položku grafu defaultní, bez podrobnější znalost věci
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemDefault(string key)
		{
            string name = key;
			return new TreePathItem(key, name,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading | RowProperties.DynamicSubNodeLoading));
		}
        /// <summary>
        /// Vytvoří a vrátí klíč řádku uživatele RootNodeUser\(login + secureId), nebo null
        /// </summary>
        /// <param name="login">Login uživatele</param>
        /// <param name="secureId">SecureID uživatele</param>
        /// <param name="absolutePath">Požadavek na absolutní cestu = začínající lomítkem</param>
        /// <returns></returns>
        internal static string CreateNodeSpidKey(string dbName, int sPId, bool absolutePath)
        {
            string key = 
                (absolutePath ? SqlServerConstants.RootNodeSqlServer : SqlServerConstants.NodeSqlServer) + 
                (!String.IsNullOrEmpty(dbName) ? "\\" + dbName : "") +
                (sPId > 0 ? "\\" + sPId.ToString("0000") : "");
            return key;
        }
        #endregion
		#region Vytvoření logické události IEventItem na základě dat z trace IEventEdge
		/// <summary>
		/// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
		/// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
		/// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private IEventItem _CreateIEventItem(IEventEdge data)
		{
            throw new InvalidOperationException("TraceReader typu SqlServer nemá vytvářet objekty IEventItem pomocí metody CreateIEventItem()!");
			return null;
		}
		#endregion
		#region IColorDeclarator Members : deklarace konkrétních barev pro názvy ColorGroups
		void IColorDeclarator.ColorDeclare(ColorDeclaratorArgs args)
		{
			// Tyto položky jsou společné s Readerem Noris.Tools.TraceVisualiser.Green.AplServer.TraceReader :
			args.Append(SqlServerConstants.ColorCommandSelect, Color.LimeGreen);
			args.Append(SqlServerConstants.ColorCommandInsert, Color.Tomato);
			args.Append(SqlServerConstants.ColorCommandUpdate, Color.Moccasin);
			args.Append(SqlServerConstants.ColorCommandDelete, Color.Violet);
			args.Append(SqlServerConstants.ColorCommandExec, Color.LightBlue);
			// Ttyo položky jsou naše:
			args.Append(SqlServerConstants.ColorLockDeadlock, Color.PaleVioletRed);
            args.Append(SqlServerConstants.ColorStoreProcedure, Color.MistyRose);
		}
		#endregion
	}
	/// <summary>
	/// Instance této třídy persistuje v ServerInfo.Tag mezi jednotlivými soubory Trace
	/// (tedy mezi jednotlivými voláními metody this._ProcessContent).
	/// Tím je umožněno předávat si kontinuální data do načítání dalšího souboru.
	/// </summary>
	internal class TraceServerPersistData
	{
		/// <summary>
		/// Vrátí objekt, pokud je to TraceServerPersistData, anebo vytvoří new() objekt
		/// </summary>
		/// <param name="tag"></param>
		/// <returns></returns>
		internal static TraceServerPersistData GetFrom(object tag)
		{
			TraceServerPersistData data = null;
			if (tag != null && tag is TraceServerPersistData)
				data = tag as TraceServerPersistData;
			if (data == null)
				data = new TraceServerPersistData();
			return data;
		}
		/// <summary>
		/// Text Provider, typicky 
		/// </summary>
		internal string Provider { get; set; }
		/// <summary>
		/// Text ServerInformation = jméno SQL serveru
		/// </summary>
		internal string ServerInformation { get; set; }
        #region Podpora filtrace
        /// <summary>
        /// Metoda vrátí true, pokud daný SQL event se má dostat do zobrazování.
        /// </summary>
        /// <param name="sqlEventId"></param>
        /// <param name="document"></param>
        /// <returns></returns>
        internal bool IsEventAllowed(string sqlEventId, string sqlEventName, TraceDocument document)
        {
            if (String.IsNullOrEmpty(sqlEventId)) return false;
            SqlEventFilter filter;
            if (this.SqlEvents.TryGetValue(sqlEventId, out filter))
                return filter.Enabled;

            filter = SqlEventFilter.CreateEnable(sqlEventId, sqlEventName);
            this.SqlEvents.Add(sqlEventId, filter);
            return true;
        }
        /// <summary>
        /// Autoinicializační seznam SQL ID a jejich hodnot Enabled (pro vizualizaci)
        /// </summary>
        internal Dictionary<string, SqlEventFilter> SqlEvents
        {
            get
            {
                if (this._SqlEvents == null)
                    this._SqlEvents = CreateEmptySqlEvents();
                return this._SqlEvents;
            }
        }
        /// <summary>
        /// Vytvoří a vrátí seznam SQL ID příkazů, které se nemají vizualizovat (je jich moc, jsou zbytečné).
        /// </summary>
        /// <returns></returns>
        internal static Dictionary<string, SqlEventFilter> CreateEmptySqlEvents()
        {
            SqlEventFilter[] disabled = new SqlEventFilter[] {
                SqlEventFilter.CreateDisable("11", "RPC:Starting"),
                SqlEventFilter.CreateDisable("13", "SQL:BatchStarting"),
                // SqlEventFilter.CreateDisable("14", "Audit Login"),
                SqlEventFilter.CreateDisable("15", "Audit Logout"),
                SqlEventFilter.CreateDisable("34", "SP:CacheMiss"),
                SqlEventFilter.CreateDisable("35", "SP:CacheInsert"),
                SqlEventFilter.CreateDisable("38", "SP:CacheHit"),
                SqlEventFilter.CreateDisable("44", "SP:StmtStarting"),
                SqlEventFilter.CreateDisable("50", "SQLTransaction"),
                SqlEventFilter.CreateDisable("54", "TransactionLog"),
                SqlEventFilter.CreateDisable("65533", "Trace Stop"),
                SqlEventFilter.CreateDisable("65534", "Trace Start")
            };

            Dictionary<string, SqlEventFilter> sqlEvents = new Dictionary<string, SqlEventFilter>();
            foreach (SqlEventFilter filt in disabled)
                sqlEvents.Add(filt.Id, filt);
            return sqlEvents;
        }
        private Dictionary<string, SqlEventFilter> _SqlEvents;
        internal class SqlEventFilter
        {
            internal static SqlEventFilter CreateDisable(string id)
            {
                return new SqlEventFilter() { Id = id, Name = null, Enabled = false };
            }
            internal static SqlEventFilter CreateDisable(string id, string name)
            {
                return new SqlEventFilter() { Id = id, Name = name, Enabled = false };
            }
            internal static SqlEventFilter CreateEnable(string id, string name)
            {
                return new SqlEventFilter() { Id = id, Name = name, Enabled = true };
            }
            internal string Id { get; set; }
            internal string Name { get; set; }
            internal bool Enabled { get; set; }
            public override string ToString()
            {
                return this.Id + ": " + this.Name + "  [" + (this.Enabled ? "x" : " ") + "]";
            }
        }
        #endregion
    }
	#endregion
	#region Konstanty NorisBlockingConstants
	/// <summary>
	/// Konstanty používané v datech trace blokování
	/// </summary>
	public class SqlServerConstants
	{
		/// <summary>
        /// SqlServerTrace (druh dat v trace souboru)
		/// </summary>
        public const string TraceType = "SqlServerTrace";
		/// <summary>
        /// 60_SqlServer
		/// </summary>
		internal const string NodeSqlServer = "60_SqlServer";
		/// <summary>
        /// \60_SqlServer
		/// </summary>
        internal const string RootNodeSqlServer = "\\" + NodeSqlServer;
        /// <summary>
        /// MsSqlServer = název logického serveru v TraceData
        /// </summary>
        internal const string ServerName = "MsSqlServer";

        internal const string ColorRoot = "Sql";
        internal const string ColorOther = "Sql.Other";
        internal const string ColorRpc = "Sql.RPC";
        internal const string ColorExistConnect = "Sql.ExistingConnect";
        internal const string ColorLogin = "Sql.AuditLogin";
        internal const string ColorLogout = "Sql.AuditLogout";
        internal const string ColorStoreProcedure = "Sql.StoreProcedure";
        internal const string ColorCommand = "Sql.Command";
		internal const string ColorCommandDot = "Sql.";
		internal const string ColorCommandSelect = "Sql.Select";
		internal const string ColorCommandInsert = "Sql.Insert";
		internal const string ColorCommandUpdate = "Sql.Update";
		internal const string ColorCommandDelete = "Sql.Delete";
		internal const string ColorCommandExec = "Sql.Exec";
		internal const string ColorLockDeadlock = "Sql.LockDeadlock";

    }
	#endregion
}
